package sk.ygor.raynor.client.test;

import com.google.gwt.json.client.*;
import static junit.framework.Assert.*;

public class JSONEquality {

    private final JSONValue value;

    private JSONEquality(JSONValue value) {
        this.value = value;
    }

    public static JSONEquality eq(JSONValue value) {
        return new JSONEquality(value);
    }

    private boolean compareJSON(JSONValue one, JSONValue two) {
        if (one instanceof JSONNull && two instanceof JSONNull) {
            return true;
        }
        if (one instanceof JSONString && two instanceof JSONString) {
            return compareJSON((JSONString) one, (JSONString) two);
        }
        if (one instanceof JSONNumber && two instanceof JSONNumber) {
            return compareJSON((JSONNumber) one, (JSONNumber) two);
        }
        if (one instanceof JSONBoolean && two instanceof JSONBoolean) {
            return compareJSON((JSONBoolean) one, (JSONBoolean) two);
        }
        if (one instanceof JSONArray && two instanceof JSONArray) {
            return compareJSON((JSONArray) one, (JSONArray) two);
        }
        if (one instanceof JSONObject && two instanceof JSONObject) {
            return compareJSON((JSONObject) one, (JSONObject) two);
        }
        return false;
    }

    private boolean compareJSON(JSONString one, JSONString two) {
        return one.stringValue().equals(two.stringValue());
    }

    private boolean compareJSON(JSONNumber one, JSONNumber two) {
        return one.doubleValue() == two.doubleValue();
    }

    private boolean compareJSON(JSONBoolean one, JSONBoolean two) {
        return one.booleanValue() == two.booleanValue();
    }

    private boolean compareJSON(JSONArray one, JSONArray two) {
        if (one.size() != two.size()) {
            return false;
        } else {
            for (int i = 0; i < one.size(); i++) {
               if (!compareJSON(one.get(i), two.get(i))) {
                   return false;
               }
            }
        }
        return true;
    }

    private boolean compareJSON(JSONObject one, JSONObject two) {
        if (one.size() != two.size()) {
            return false;
        } else {
            for (String key : one.keySet()) {
                if (!compareJSON(one.get(key), two.get(key))) {
                    return false;
                }
            }
        }
        return true;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof JSONEquality) {
            return compareJSON(value, ((JSONEquality) obj).value);
        } else {
            return false;
        }
    }

    @Override
    public String toString() {
        return value.toString();
    }

    public static void assertEqualJson(JSONValue expected, JSONValue actual) {
        assertEquals(eq(expected), eq(actual));
    }

    public static void assertNotEqualJson(JSONValue one, JSONValue two) {
        if (eq(one).equals(eq(two))) {
            fail("Must not be equal: \n" + one + "\n" + two);
        }
    }
}
